import { Helper } from "../core/helper";

const MOD_ADLER32: number = 0xFFF1;
const MOD_ADLER64: bigint = BigInt(Helper.uint(0xFFFFFFFB));

class ADLER32_CTX {
  a : number;
  b : number;
}
class ADLER64_CTX {
  a : bigint;
  b : bigint;
}
export class ADLER {

  static Init32(): ADLER32_CTX {
    var ctx = new ADLER32_CTX();
    ctx.a = 1;
    ctx.b = 0;
    return ctx;
  }
  static Init64(): ADLER64_CTX {
    var ctx = new ADLER64_CTX();
    ctx.a = BigInt(1);
    ctx.b = BigInt(0);
    return ctx;
  }
  static Update32(ctx: ADLER32_CTX, data:number[], length: number){
    let i;
    for (i =0; i != length; i++){
      ctx.a = (ctx.a + data[i]) % MOD_ADLER32;
      ctx.b = (ctx.b + ctx.a) % MOD_ADLER32;
    }
  }
  static Final32(ctx: ADLER32_CTX): number[] {
    let md: number[] = [];
    Helper.CopyFrom4Reverse(md, ctx.b << 16 | ctx.a, 0);
    return md;
  }
  static Update64(ctx: ADLER64_CTX, data:number[], length: number){
    let i;
    for (i =0; i != length; i++){
      ctx.a = (ctx.a + BigInt(data[i])) % MOD_ADLER64;
      ctx.b = (ctx.b + ctx.a) % MOD_ADLER64;
    }
  }
  static Final64(ctx: ADLER64_CTX): number[] {
    let md: number[] = [];
    Helper.CopyFrom8Reverse(md, ctx.b << BigInt(32) | ctx.a, 0);
    return md;
  }
}
export class ADLER32 {
  static JustMake(data: number[]): string {
    var ctx = ADLER.Init32();
    ADLER.Update32(ctx, data,data.length);
    return Helper.ToHex(ADLER.Final32(ctx));
  }
  _ctx: ADLER32_CTX;
  New() : void{
    this._ctx = ADLER.Init32();
  }
  Update(data: number[]): void {
    ADLER.Update32(this._ctx, data, data.length);
  }
  Make(): string{
    return Helper.ToHex(ADLER.Final32(this._ctx));
  }
  MakeUint32(): number {
    return Helper.byte2uint(ADLER.Final32(this._ctx), 0);
  }
}
export class ADLER64 {
  static JustMake(data: number[]): string {
    var ctx = ADLER.Init64();
    ADLER.Update64(ctx, data,data.length);
    return Helper.ToHex(ADLER.Final64(ctx));
  }
  _ctx: ADLER64_CTX;
  New() : void{
    this._ctx = ADLER.Init64();
  }
  Update(data: number[]): void {
    ADLER.Update64(this._ctx, data, data.length);
  }
  Make(): string{
    return Helper.ToHex(ADLER.Final64(this._ctx));
  }
  MakeUint32(): bigint {
    return Helper.byte2ulong(ADLER.Final64(this._ctx), 0);
  }
}